#include "BinanceTriangle.h"
#include <iostream>

namespace {
    std::string getRoadKey(const std::string& str1, const std::string& str2, const std::string& str3) {
        std::vector<std::string> strings = {str1, str2, str3};
        std::sort(strings.begin(), strings.end());
        std::string result = std::accumulate(strings.begin(), strings.end(), std::string(""));
        return result;
    }
}

BinanceTriangle* BinanceTriangle::triangle = nullptr;

BinanceTriangle* BinanceTriangle::getInstance(const std::string& apiKey,const std::string& secretKey) {
    if (triangle == nullptr) {
        triangle = new BinanceTriangle(apiKey,secretKey);
    }
    return triangle;
}

BinanceTriangle::BinanceTriangle(const std::string& apiKey,const std::string& secretKey) {
    flag = false;
    allVolume = 0.0;
    dataPtr = BinanceData::getInstance();
    tradePtr = BinanceTrade::getInstance(apiKey,secretKey);
    accountPtr = BinanceAccount::getInstance(apiKey,secretKey);
    restApi = new BinanceRestApi(apiKey,secretKey);
    restApi->allTradingPairs(allPairs, allCoins);
    restApi->getSymbolInfo(minQty);
    std::map<std::string,double> coinBalance;
    restApi->getAccountBalance(coinBalance);
    accountPtr->initBalance(coinBalance);
    logi("allPairs:{}",allPairs.size());
    logi("allCoins:{}",allCoins.size());
    for (int i = 0; i < startCoins.size(); i++) {
        for (int j = 0; j < allCoins.size(); j++) {
            for (int k = 0; k < allCoins.size(); k++) {
                if (startCoins[i] == allCoins[j] || startCoins[i] == allCoins[k] || j == k) continue;
                std::string AB, BC, CA;
                char atb, btc, cta;
                // A B 交易对
                if (allPairs.count(startCoins[i] + allCoins[j]) > 0) {
                    AB = startCoins[i] + allCoins[j];
                    atb = 'b';
                }
                else if (allPairs.count(allCoins[j] + startCoins[i]) > 0) {
                    AB = allCoins[j] + startCoins[i];
                    atb = 'a';
                }
                else {
                    continue;
                }
                // B C 交易对
                if (allPairs.count(allCoins[j] + allCoins[k]) > 0) {
                    BC = allCoins[j] + allCoins[k];
                    btc = 'b';
                }
                else if (allPairs.count(allCoins[k] + allCoins[j]) > 0) {
                    BC = allCoins[k] + allCoins[j];
                    btc = 'a';
                }
                else {
                    continue;
                }
                // C A 交易对
                if (allPairs.count(allCoins[k] + startCoins[i]) > 0) {
                    CA = allCoins[k] + startCoins[i];
                    cta = 'b';
                }
                else if (allPairs.count(startCoins[i] + allCoins[k]) > 0) {
                    CA = startCoins[i] + allCoins[k];
                    cta = 'a';
                }
                else {
                    continue;
                }
                Road r = { startCoins[i], allCoins[j], allCoins[k], AB, atb, BC, btc, CA, cta };
                std::string roadKey_ = getRoadKey(AB+atb,BC+btc,CA+cta);
                if(roadSet.count(roadKey_) == 0){
                    roadSet.insert(roadKey_);
                    roads.push_back(r);
                    std::cout<<"[add]:"<<roadKey_<<std::endl;
                }
            }
        }
    }
    logi("allRoads:{}",roads.size());
}

std::string BinanceTriangle::convertToValidQuantity(const std::string& symbol, double amount) {
    double minQuantity = minQty[symbol];
    int decimalPlaces = 0;
    if(minQuantity < 1){
        decimalPlaces = static_cast<int>(std::abs(std::floor(std::log10(minQuantity))));
    }
    double truncatedAmount = std::floor(amount * std::pow(10, decimalPlaces)) / std::pow(10, decimalPlaces);
    std::ostringstream oss;
    oss << std::fixed << std::setprecision(decimalPlaces) << truncatedAmount;
    return oss.str();
}

void BinanceTriangle::checkArbitrage() {
    while (true) {
        double a, b, c, d;
        for (int i = 0; i < roads.size(); i++) {
            d = 1.0;
            Road r = roads[i];
            a = dataPtr->getPrice(r.AB, r.atb);
            if(a<0) continue;
            b = dataPtr->getPrice(r.BC, r.btc);
            if(b<0) continue;
            c = dataPtr->getPrice(r.CA, r.cta);
            if(c<0) continue;

            r.atb == 'b' ? d /= a : d *= a;
            r.btc == 'b' ? d /= b : d *= b;
            r.cta == 'b' ? d /= c : d *= c;

            if (d < 0.997 && d > 0.9) {
                std::cout << "=============================" << std::endl;
                logi("{}:{} {}:{} {}:{} grid:{}", r.AB, a, r.BC, b, r.CA, c, d);
                ord.type = "MARKET";
                double Amount_A,Amount_B,Amount_C;
                Amount_A = initMount[r.A];
                if(accountPtr->getBalance(r.A) < Amount_A){
                    continue;
                }
                ord.symbol = r.AB;
                if(r.atb == 'b'){
                    ord.side = "SELL";
                    ord.sQuantity = convertToValidQuantity(r.AB,Amount_A);
                    Amount_B = std::stod(ord.sQuantity) * a * 0.995;
                }else{
                    ord.side = "BUY";
                    Amount_B = Amount_A / a;
                    ord.sQuantity = convertToValidQuantity(r.AB,Amount_B);
                    Amount_B = std::stod(ord.sQuantity);
                }
                //tradePtr->place_order(ord);
                restApi->send_order(ord);

                ord.symbol = r.BC;
                if(r.btc == 'b'){
                    ord.side = "SELL";
                    ord.sQuantity = convertToValidQuantity(r.BC,Amount_B);
                    Amount_C = std::stod(ord.sQuantity) * b * 0.995;
                }else{
                    ord.side = "BUY";
                    Amount_C = Amount_B / (b * 1.005);
                    ord.sQuantity = convertToValidQuantity(r.BC,Amount_C);
                    Amount_C = std::stod(ord.sQuantity);
                }
                //std::this_thread::sleep_for(std::chrono::milliseconds(10));
                tradePtr->place_order(ord);

                ord.symbol = r.CA;
                if(r.cta == 'b'){
                    ord.side = "SELL";
                    ord.sQuantity = convertToValidQuantity(r.CA,Amount_C);
                }else{
                    ord.side = "BUY";
                    Amount_A = Amount_C / (c * 1.005);
                    ord.sQuantity = convertToValidQuantity(r.CA,Amount_A);
                }
                //clearstd::this_thread::sleep_for(std::chrono::milliseconds(50));
                tradePtr->place_order(ord);
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
        }
    }
}

